/**
 * 多线程复制图形界面
 * 文件复制速度主要根据硬盘转速和cpu 处理速度来决定
 * 
 */
package com.jahentao.integrationTest.case13;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Properties;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.UIManager;

/**
 * @author jahen
 * @date 2016年8月1日
 * @time 下午2:56:11
 * @category Socket编程
 * TODO
 * 实现暂停功能
 * 实现断点续传
 * 
 * 在网络上测试
 * 由于没有实现一些协议的原因，网络上mp3链接地址都是要转换或者是重定向的
 * 如：http://sh.ctfs.ftn.qq.com/ftn_handler/d8152bfaa7b73f558c292b3071ee771acee669d481d153a5334966822b3efc3056887caccdd50623943994f73e2581254486f52be38fc5ca508528baf14e4e50/?fname=%E5%BC%A0%E6%99%BA%E9%9C%96%20-%20%E5%90%8E%E6%9D%A5.mp3&k=7f6236302468079e08c6c9581061514e5a5a05550e585a55145153050e4c57530a071b090751004c5f575703535300590d560252367063b4fcb4f1f1ee414e418391f684180c1352395f&fr=00&&txf_fid=d7e7cf3a890b6f09c86190a0c140ef82a4dc36d0&xffz=10221294
 * 上传到qq中转站的链接，显示没有权限
 * 
 * 使用一些 外链生成的mp3链接
 * 如：http://wl.baidu190.com/1471309839/643341CA869BF87B35E7092103D25C2E.mp3
 * 可以下载，并播放。说明基本Socket功能还是实现成功了的
 * 
 * TODO
 * - 有个BUG暂时没有改
 * 	   当多次点击取消和开始按钮时，有bug
 * - 在连接被断开时主动重连
 * - 支持配置cookie等信息
 */
public class MultiThreadDownloadJFrame extends JFrame implements ActionListener{
	
	private static final long serialVersionUID = 1668650877911746997L;
	private static final int MAX_THREAD_NUM = 64;
	private URL url;
	private String outPath;
	private int threadNum;
	private JLabel[] llthreads;
	private JProgressBar[] pbars;
	
	private volatile boolean isStarted;
	private volatile boolean isPaused;
	private volatile boolean isFinished;
	
	public boolean isFinished() {
		return isFinished;
	}

	private JButton btnStart;
	private JButton btnCancel;
	private JButton btoutPath;
	private JTextField tfthreadNum;
	private JTextField tfoutPath;
	private JTextField tfsrcFile;
	private DownloadService downloadService;
	@SuppressWarnings("unused")
	private File metadataFile;
	
	public JProgressBar[] getPbars() {
		return pbars;
	}

	public MultiThreadDownloadJFrame(){
		init();
	}
	
	private void init() {
		//优化UI界面 采取平台的外观
		if(UIManager.getLookAndFeel().isSupportedLookAndFeel()){
			final String platform = UIManager.getSystemLookAndFeelClassName();
			// If the current Look & Feel does not match the platform Look & Feel,
			// change it so it does.
			if (!UIManager.getLookAndFeel().getName().equals(platform)) {
				try {
					UIManager.setLookAndFeel(platform);
				} catch (Exception exception) {
					exception.printStackTrace();
				}
			}
		}
	
		this.setTitle("多线程下载");
		this.setBounds(200, 200, 530, 250);
		this.setLayout(null); //设置绝对布局
		
		//srcFile
		JLabel llsrcFile = new JLabel("URL");
		llsrcFile.setBounds(10, 30, 70, 30);
		tfsrcFile = new JTextField();
		tfsrcFile.setBounds(80, 30, 300, 30);
		this.add(llsrcFile);
		this.add(tfsrcFile);

		//outPath
		JLabel lloutPath = new JLabel("输出目录");
		lloutPath.setBounds(10, 70, 70, 30);
		tfoutPath = new JTextField();
		tfoutPath.setBounds(80, 70, 300, 30);
		btoutPath = new JButton("选择目录");
		btoutPath.setBounds(400, 70, 100, 30);
		this.add(lloutPath);
		this.add(tfoutPath);
		this.add(btoutPath);
		btoutPath.addActionListener(this);
		
		//threadNum
		JLabel llthreadNum= new JLabel("线程数");
		llthreadNum.setBounds(10, 110, 70, 30);
		tfthreadNum = new JTextField();
		tfthreadNum.setBounds(80, 110, 50, 30);
		this.add(llthreadNum);
		this.add(tfthreadNum);
		
		btnStart = new JButton("开始");
		btnCancel = new JButton("取消");
		btnReset = new JButton("重置");
		btnStart.setBounds(150, 150, 70, 30);
		btnCancel.setBounds(230, 150, 70, 30);
		btnReset.setBounds(310, 150, 70, 30);
		this.add(btnStart);
		this.add(btnCancel);
		this.add(btnReset);
		btnStart.addActionListener(this);
		btnCancel.addActionListener(this);
		btnReset.addActionListener(this);
		
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				System.exit(-1);
			}
		});
		this.setResizable(false);
		this.setVisible(true);
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource()==btnStart){	// 点击开始按钮
			isFinished = false;
			try {
				if(!isStarted){
					finishBarCount = 0;
					if(isPaused){
						isPaused = !isPaused;
						downloadService.startCopy(true);//开始继续复制
					}
					else{
						this.threadNum = Integer.parseInt(tfthreadNum.getText().trim());
						
						try {
							this.url = new URL(tfsrcFile.getText().trim());
						} catch (MalformedURLException e1) {
							e1.printStackTrace();
						}
						
						this.outPath = tfoutPath.getText().trim();
						File[] files = new File(outPath).listFiles();
						// 寻找下载记录文件
						/*
						 * 规定记录文件和下载文件在同一个输出目录
						 */
						Properties properties = null;
						for(File f : files) {
							if(f.getName().endsWith(".download")){
								String name = f.getName();
								String downloadFileName = name.substring(0, name.lastIndexOf(".download"));
								if(url.getPath().endsWith(downloadFileName)) {
									properties = new Properties();
									metadataFile = f;
									FileInputStream fis = new FileInputStream(f);
									properties.load(fis);
									fis.close();
									break;
								}
							}
						}
						
						// TODO 单个线程可复制最大量的限制
						if(threadNum < 1){
							JOptionPane.showMessageDialog(this, "开始下载，线程数不能小于1", "错误", JOptionPane.ERROR_MESSAGE); 
							return;
						}else if(threadNum > MAX_THREAD_NUM){
							JOptionPane.showMessageDialog(this, "开启线程数过多", "警告", JOptionPane.WARNING_MESSAGE); 
							return;
						}
						/*if(url.length()/threadNum > Integer.MAX_VALUE){
							JOptionPane.showMessageDialog(this, "单个线程复制的量超过上限，务必增加线程数", "错误", JOptionPane.ERROR_MESSAGE); 
						}*/
						DownloadInfo downloadInfo = new DownloadInfo();
						
						if(properties==null) { // 没有记录文件，属于新下载项
							this.llthreads = new JLabel[threadNum];
							this.pbars = new JProgressBar[threadNum];
							
							downloadInfo.setUrl(url);
							downloadInfo.setOutPath(outPath);
							downloadInfo.setThreadNum(threadNum);
							downloadInfo.setMetadataFile(new File(outPath+url.getPath())); // 先传过去，再修改
							
							downloadService = new DownloadService(downloadInfo,this);
							addJProcessBarsDynamically(downloadService.getInfoList()); // 添加进度条
							
							properties = downloadService.getProperties();
							downloadInfo.setMetadataFile(metadataFile = new File(properties.getProperty("metadataFile"))); // 修改为正确的metadataFile
							
							downloadService.startCopy(false);//从起始开始下载
						}
						else { // 断点续传
							threadNum = Integer.parseInt(properties.getProperty("threadNum"));
							tfthreadNum.setText(""+threadNum); // 修改正确线程数 // 当添加进度条时，一起重绘
//							tfthreadNum.setEnabled(false);
							
							this.llthreads = new JLabel[threadNum];
							this.pbars = new JProgressBar[threadNum];
							
							downloadService = new DownloadService(properties,this); //新建断点续传
							if (addJProcessBarsDynamically(downloadService.getInfoList())) {  // 添加进度条，可能断点续传任务已经完成
								return;
							}
							downloadService.startCopy(false);//开启线程,开始下载
						}
					}
					isStarted = !isStarted;
					btnStart.setText("暂停");
				}else{
					isStarted = !isStarted;
					downloadService.pauseCopy(false); // 暂停
					isPaused = !isPaused;
					btnStart.setText("开始");
				}
			} catch (Exception e2) {
				e2.getMessage();
			}
		}
		else if(e.getSource()==btnCancel){ // 点击取消按钮
			if (!isStarted) {
				return;
			}
			if (!isFinished) {
				JOptionPane.showMessageDialog(this, "下载任务取消，保存记录文件，下次开始时可断点续传", "提示", JOptionPane.CLOSED_OPTION); 
				downloadService.pauseCopy(true);
			}
			//状态归零
			isStarted = false;
			isPaused = false;
			btnStart.setText("开始");
			finishAndRemoveProgressBars();
		}
		else if(e.getSource()==btoutPath){ //选择原文件
			JFileChooser jf = new JFileChooser("F:/test/");
			jf.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);//设置过滤
			jf.showOpenDialog(null);
			tfoutPath.setText(jf.getSelectedFile().getAbsolutePath());
		}
		else if(e.getSource()==btnReset){ //重置
			tfsrcFile.setText("");
			tfoutPath.setText("");
			tfthreadNum.setText("");
//			btnCancel.doClick();
		}
	}
	
	/**
	 * 动态添加进度条
	 */
	private boolean addJProcessBarsDynamically(List<DownloadInfo> infoList){
		for(int i=0; i<threadNum; i++) {
			llthreads[i] = new JLabel("Thread-"+(i+1));
			llthreads[i].setBounds(20, 200 + i*35, 500, 30);
			this.add(llthreads[i]);
			
			pbars[i] = new JProgressBar();
			pbars[i].setBounds(100, 200 + i*35, 410, 30);
			
			DownloadInfo info = infoList.get(i);
			int value = (int)(info.getAmount());
			pbars[i].setMaximum((int) (info.getEnd() - info.getStart())); // 这就限制了单个线程复制的区域大小在2G之内
			pbars[i].setValue(value);
			
			if (value == (info.getEnd() - info.getStart())) {
				finishBarCount ++;
				if (judgedFinished()) {
					return true;
				}
			}
			this.add(pbars[i]);
		}
		int lastHeight = 200 + threadNum*35 + 50; //50是底边框预览长度
		this.setSize(this.getWidth(), (lastHeight>this.getHeight())?lastHeight:this.getHeight());
		this.repaint();
		return false;
	}
	
	/**
	 * 更新进度条
	 */
	private int finishBarCount=0;
	private JButton btnReset;
	public boolean updateProgressBars(int index, int length) {
		pbars[index].setValue(pbars[index].getValue() + length);
		this.repaint();
//		System.out.println(index + " : " + pbars[index].getValue() + "/" + pbars[index].getMaximum());
		if(pbars[index].getValue() >= pbars[index].getMaximum()){
			finishBarCount++;
			return judgedFinished();
		}
		return false;
	}
	
	/**
	 * 判断是否结束
	 * <p>结束则删除信息记录文件并删除进度条
	 */
	public boolean judgedFinished() {
		boolean finished = finishBarCount >= pbars.length;
		
		if(finished){
			isFinished = true;
			//完成后，删除信息记录文件
			Properties properties = downloadService.getProperties();
			File metadataFile = new File(properties.getProperty("metadataFile"));
			metadataFile.delete();
			JOptionPane.showMessageDialog(this, "下载完成", "恭喜！", JOptionPane.CLOSED_OPTION); 
			// 删除进度条
			finishAndRemoveProgressBars();
		}
		
		return finished;
	}
	
	/**
	 * 完成后删除进度条
	 */
	public void finishAndRemoveProgressBars() {
		for(int i=0; i<pbars.length; i++){
			this.remove(llthreads[i]);
			this.remove(pbars[i]);
		}
		this.setSize(this.getWidth(), 250);
		this.repaint();
		finishBarCount=0;
		isStarted = false; //完成
		btnStart.setText("开始");
	}
}
